<template>
    <div class="main-container">
        <!-- 左边区域：画面内容 -->
        <div class="left-panel">
            <div class="video-section">
                <div class="video-container">
                    <video ref="videoElement" autoplay muted playsinline @canplay="setupDetection"
                        class="video-element"></video>
                    <canvas ref="displayCanvas" class="detection-canvas"></canvas>
                </div>
            </div>
            <div class="button-section">
                <el-button type="primary" size="small" class="action-button" @click="toggleDetection">
                    <i :class="isRunning ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
                    {{ isRunning ? '停止检测' : '开始检测' }}
                </el-button>
                <el-switch
                    v-model="useNetworkCamera"
                    active-text="网络摄像头"
                    inactive-text="本地摄像头"
                    @change="handleCameraSwitch"
                />
                <!-- 本地摄像头选择 -->
                <el-select
                    v-if="!useNetworkCamera"
                    v-model="selectedCameraId"
                    placeholder="选择摄像头"
                    size="small"
                    style="width: 200px;"
                    @change="handleCameraChange"
                >
                    <el-option
                        v-for="device in cameraDevices"
                        :key="device.deviceId"
                        :label="device.label"
                        :value="device.deviceId"
                    />
                </el-select>
                <!-- 网络摄像头输入 -->
                <el-input
                    v-if="useNetworkCamera"
                    v-model="networkCameraUrl"
                    placeholder="请输入网络摄像头地址"
                    size="small"
                    style="width: 200px;"
                    @keyup.enter.native="handleCameraSwitch"
                >
                    <el-button slot="append" @click="handleCameraSwitch">连接</el-button>
                </el-input>
                <div class="fps-counter" v-if="isRunning">
                    FPS: {{ currentFps.toFixed(1) }}
                </div>
            </div>
        </div>

        <!-- 右边区域：检测结果和图表 -->
        <div class="right-panel">
            <div class="description-section">
                <div class="alert-container">
                    <el-empty v-if="detections.length === 0" :image-size="180"></el-empty>
                    <transition-group v-else name="list" tag="div">
                        <el-alert v-for="detection in detections" :key="detection.id"
                            :title="detection.class_name"
                            :type="getAlertType(detection.confidence)"
                            :description="`置信度: ${(detection.confidence * 100).toFixed(1)}%`"
                            :closable="false" show-icon
                            class="alert-item">
                        </el-alert>
                    </transition-group>
                </div>
            </div>
            <div class="visualization-section">
                <div v-if="detectionStats.data.length === 0" class="no-results">
                    <el-empty description="暂无数据" :image-size="200"></el-empty>
                </div>
                <div v-else id="detectionChart" style="width: 100%; height: 100%;"></div>
            </div>
        </div>
    </div>
</template>

<script>
import * as echarts from 'echarts';
import { startDetection, stopDetection, getDetectionStatus } from '@/api/inspection'
import { getRealtimeLastData, sendDingTalkMessage, getDingTalkConfig } from '@/api/dingtalk'

export default {
    name: 'YoloDetection',

    data() {
        return {
            isRunning: false,
            webSocket: null,
            videoStream: null,
            detections: [],
            animationFrameId: null,
            lastFrameTime: 0,
            frameCount: 0,
            currentFps: 0,
            fpsUpdateInterval: null,
            chart: null,
            detectionStats: {
                timestamps: [],
                data: []
            },
            lastTimestamp: null,
            useNetworkCamera: false,
            networkCameraUrl: 'http://109.206.96.58:8080/cam_1.cgi', // 默认值，可手动修改
            cameraDevices: [], // 存储本地摄像头设备列表
            selectedCameraId: '', // 当前选中的摄像头ID
            dingTalkConfig: {
                isActive: false
            },
            isComponentDestroying: false  // 添加标志位
        };
    },

    methods: {
        // 获取本地摄像头列表
        async getCameraDevices() {
            try {
                const devices = await navigator.mediaDevices.enumerateDevices();
                this.cameraDevices = devices.filter(device => device.kind === 'videoinput');
                
                // 如果有摄像头，默认选择第一个
                if (this.cameraDevices.length > 0) {
                    this.selectedCameraId = this.cameraDevices[0].deviceId;
                }
                
                console.log('可用摄像头:', this.cameraDevices);
            } catch (error) {
                console.error('获取摄像头列表失败:', error);
                this.$message.error('获取摄像头列表失败');
            }
        },

        // 初始化摄像头
        async initCamera() {
            // 停止现有流
            if (this.videoStream) {
                this.videoStream.getTracks().forEach(track => track.stop());
                this.videoStream = null;
            }

            const video = this.$refs.videoElement;
            if (video) {
                video.srcObject = null;
                video.src = ''; // 清除之前的 src
            }

            if (this.useNetworkCamera) {
                // 网络摄像头
                if (!this.networkCameraUrl) {
                    this.$message.error('请输入网络摄像头地址');
                    return;
                }
                video.src = this.networkCameraUrl;
                video.play().catch(err => {
                    console.error('无法播放网络摄像头流:', err);
                    this.$message.error('无法连接网络摄像头，请检查地址或格式');
                });
            } else {
                // 本地摄像头
                try {
                    const constraints = {
                        video: {
                            deviceId: this.selectedCameraId ? { exact: this.selectedCameraId } : undefined,
                            width: { ideal: 640 },
                            height: { ideal: 480 },
                            frameRate: { ideal: 15 }
                        }
                    };
                    
                    const stream = await navigator.mediaDevices.getUserMedia(constraints);
                    this.videoStream = stream;
                    video.srcObject = stream;
                    console.log('本地摄像头已打开');
                } catch (err) {
                    console.error('无法访问本地摄像头:', err);
                    this.$message.error('无法访问本地摄像头，请检查设备和权限');
                }
            }
        },

        // 处理摄像头切换
        async handleCameraChange() {
            if (this.isRunning) {
                this.stopDetection();
            }
            await this.initCamera();
        },

        // 处理摄像头类型切换
        async handleCameraSwitch() {
            if (this.isRunning) {
                this.stopDetection();
            }
            await this.initCamera();
        },

        setupDetection() {
            if (!this.webSocket) {
                this.initWebSocket();
            }
        },

        toggleDetection() {
            if (this.isRunning) {
                this.stopDetection();
            } else {
                this.startDetection();
            }
        },

        startDetection() {
            if (!this.$refs.videoElement.src && !this.$refs.videoElement.srcObject) {
                this.initCamera();
            }

            this.isRunning = true;
            this.lastFrameTime = performance.now();
            this.frameCount = 0;

            this.fpsUpdateInterval = setInterval(() => {
                const now = performance.now();
                const elapsed = now - this.lastFrameTime;
                this.currentFps = this.frameCount / (elapsed / 1000);
                this.lastFrameTime = now;
                this.frameCount = 0;
            }, 1000);

            this.processFrame();
        },

        stopDetection() {
            this.isRunning = false;
            if (this.animationFrameId) {
                cancelAnimationFrame(this.animationFrameId);
                this.animationFrameId = null;
            }
            if (this.fpsUpdateInterval) {
                clearInterval(this.fpsUpdateInterval);
                this.fpsUpdateInterval = null;
            }
            this.detections = [];

            // 关闭WebSocket连接
            if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
                this.webSocket.close(1000, "用户停止检测");
                this.webSocket = null;
            }

            if (this.videoStream) {
                this.videoStream.getTracks().forEach(track => track.stop());
                this.videoStream = null;
                console.log('本地摄像头已关闭');
            }
            const video = this.$refs.videoElement;
            if (video && this.useNetworkCamera) {
                video.src = ''; // 清除网络摄像头流
                console.log('网络摄像头已断开');
            }

            // 只有在组件正常停止检测时才发送消息，而不是在组件销毁时
            if (this.dingTalkConfig.isActive && !this.isComponentDestroying) {
            setTimeout(async () => {
                try {
                    const response = await getRealtimeLastData();
                    if (response.data) {
                    await sendDingTalkMessage({
                            type: 'realtime',
                            data: response.data
                        });
                        }
                } catch (error) {
                    console.error('发送钉钉消息失败:', error);
                }
            }, 5000);
            }
        },

        processFrame() {
            if (!this.isRunning) return;

            const video = this.$refs.videoElement;
            const canvas = this.$refs.displayCanvas;
            const ctx = canvas.getContext('2d');

            // 设置 canvas 尺寸与视频一致
            canvas.width = video.videoWidth || 640;
            canvas.height = video.videoHeight || 480;

            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
            this.frameCount++;

            if (this.frameCount % 15 === 0) {
                const dataUrl = canvas.toDataURL('image/jpeg', 0.7);
                if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
                    this.webSocket.send(JSON.stringify({
                        frame: dataUrl
                    }));
                }
            }

            this.drawDetections(ctx);
            this.animationFrameId = requestAnimationFrame(() => this.processFrame());
        },

        drawDetections(ctx) {
            if (!this.detections.length) return;

            const colors = {
                person: '#FF0000',
                bicycle: '#00FF00',
                car: '#0000FF',
                default: '#FFFF00'
            };

            this.detections.forEach(detection => {
                const [x1, y1, x2, y2] = detection.bbox;
                const width = x2 - x1;
                const height = y2 - y1;
                const color = colors[detection.class_name] || colors.default;

                ctx.strokeStyle = color;
                ctx.lineWidth = 2;
                ctx.strokeRect(x1, y1, width, height);

                const label = `${detection.class_name} ${(detection.confidence * 100).toFixed(1)}%`;
                const textWidth = ctx.measureText(label).width;
                ctx.fillStyle = color;
                ctx.fillRect(x1, y1 - 20, textWidth + 10, 20);

                ctx.fillStyle = '#FFFFFF';
                ctx.font = '14px Arial';
                ctx.fillText(label, x1 + 5, y1 - 5);
            });
        },

        initWebSocket() {
            const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            const token = localStorage.getItem('token');
            if (!token) {
                this.$message.error('请登录后重试');
                return;
            }

            const wsUrl = `${wsProtocol}//localhost:8000/ws/detect/?token=${token}`;
            this.webSocket = new WebSocket(wsUrl);

            this.webSocket.onopen = () => console.log('WebSocket连接已建立');
            this.webSocket.onmessage = (event) => {
                const response = JSON.parse(event.data);
                if (response.success) {
                    this.detections = response.detections;
                    this.updateDetectionStats(response.detections);
                } else if (response.error) {
                    console.error('检测错误:', response.error);
                }
            };
            this.webSocket.onclose = (event) => {
                console.log('WebSocket连接已关闭', event.code, event.reason);
                if (event.code !== 1000 && this.isRunning) {
                    setTimeout(() => this.initWebSocket(), 1000);
                }
            };
            this.webSocket.onerror = (error) => console.error('WebSocket错误:', error);
        },

        updateDetectionStats(detections) {
            const now = Math.floor(Date.now() / 1000);
            if (this.lastTimestamp !== now) {
                this.lastTimestamp = now;
                this.detectionStats.timestamps.push(now);
                this.detectionStats.data.push(this.countDetections(detections));

                if (this.detectionStats.timestamps.length > 6) {
                    this.detectionStats.timestamps.shift();
                    this.detectionStats.data.shift();
                }
                this.renderChart();
            }
        },

        countDetections(detections) {
            const counts = {};
            detections.forEach(detection => {
                const className = detection.class_name;
                if (!counts[className]) counts[className] = 0;
                counts[className]++;
            });
            return counts;
        },

        renderChart() {
            if (!this.chart) {
                this.chart = echarts.init(document.getElementById('detectionChart'));
            }

            const option = {
                title: { 
                    text: '实时检测统计', 
                    left: 'center' 
                },
                tooltip: { 
                    trigger: 'axis', 
                    axisPointer: { 
                        type: 'shadow' 
                    } 
                },
                grid: {
                    left: '3%',
                    right: '4%',
                    bottom: '3%',
                    containLabel: true
                },
                xAxis: {
                    type: 'category',
                    boundaryGap: true,
                    data: this.detectionStats.timestamps.map(ts => new Date(ts * 1000).toLocaleTimeString()),
                    axisLabel: {
                        interval: 0,
                        rotate: 30
                    }
                },
                yAxis: { 
                    type: 'value', 
                    name: '检测次数',
                    min: 0,
                    max: 10,
                    minInterval: 1,
                    splitLine: {
                        show: true,
                        lineStyle: {
                            type: 'dashed'
                        }
                    }
                },
                series: Object.keys(this.detectionStats.data[0] || {}).map(className => ({
                    name: className,
                    type: 'bar',
                    data: this.detectionStats.data.map(d => d[className] || 0),
                    stack: '总量',
                    barGap: '30%',
                    itemStyle: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                            { offset: 0, color: '#83bff6' },
                            { offset: 0.5, color: '#188df0' },
                            { offset: 1, color: '#188df0' }
                        ])
                    }
                }))
            };

            this.chart.setOption(option);
        },

        getAlertType(confidence) {
            if (confidence >= 0.8) return 'success';
            if (confidence >= 0.6) return 'warning';
            return 'info';
        },

        cleanup() {
            this.isComponentDestroying = true;  // 设置标志位
            this.stopDetection();
            if (this.webSocket) {
                this.webSocket.close(1000, "组件卸载");
                this.webSocket = null;
            }
            if (this.chart) {
                this.chart.dispose();
                this.chart = null;
            }
        },

        async startDetect() {
            try {
                await startDetection()
                this.$message.success('开始检测')
                this.isRunning = true
                this.startWebSocket()
            } catch (error) {
                this.$message.error('启动检测失败')
                console.error('启动检测失败:', error)
            }
        },

        async stopDetect() {
            try {
                await stopDetection()
                this.$message.success('停止检测')
                this.isRunning = false
                this.stopWebSocket()
            } catch (error) {
                this.$message.error('停止检测失败')
                console.error('停止检测失败:', error)
            }
        },

        async checkStatus() {
            try {
                const response = await getDetectionStatus()
                this.isRunning = response.data.is_detecting
                if (this.isRunning) {
                    this.startWebSocket()
                }
            } catch (error) {
                console.error('获取状态失败:', error)
            }
        },

        async checkDingTalkConfig() {
            try {
                const response = await getDingTalkConfig()
                if (response.data) {
                    this.dingTalkConfig.isActive = response.data.is_active
                }
            } catch (error) {
                console.error('获取钉钉配置失败:', error)
            }
        },
    },

    async mounted() {
        // 获取摄像头列表
        await this.getCameraDevices();
        // 获取钉钉配置
        await this.checkDingTalkConfig();
    },

    beforeDestroy() {
        this.cleanup();
    }
};
</script>

<style scoped>
.main-container {
    display: flex;
    height: 100%;
    box-sizing: border-box;
    padding: 10px;
    background-color: #f5f7fa;
    gap: 20px;
}

/* 左边区域 */
.left-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 20px;
    min-height: 0; /* 防止flex子元素溢出 */
}

.video-section {
    flex: 1;
    background-color: white;
    border-radius: 8px;
    padding: 15px;
    display: flex;
    justify-content: center;
    align-items: center;
    box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
    transition: all 0.3s ease;
    min-height: 0; /* 防止flex子元素溢出 */
    overflow: hidden; /* 防止内容溢出 */
}

.video-container {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 8px;
    overflow: hidden;
    background-color: #000;
}

.video-element {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    visibility: hidden;
}

.detection-canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.button-section {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 15px;
    padding: 0 10px;
    flex-wrap: wrap;
    min-height: 40px; /* 固定最小高度 */
}

.action-button {
    min-width: 120px;
    height: 32px;
    padding: 0 15px;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    font-size: 13px;
    transition: all 0.2s ease;
}

.action-button i {
    font-size: 14px;
}

.fps-counter {
    font-size: 14px;
    color: #606266;
    background-color: #f4f4f5;
    padding: 4px 8px;
    border-radius: 4px;
}

/* 右边区域 */
.right-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 20px;
    min-height: 0; /* 防止flex子元素溢出 */
}

.description-section,
.visualization-section {
    flex: 1;
    background-color: white;
    border-radius: 8px;
    padding: 15px;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
    transition: all 0.3s ease;
    min-height: 0; /* 防止flex子元素溢出 */
    overflow: hidden; /* 防止内容溢出 */
}

.alert-container {
    overflow-y: auto;
    height: 100%;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding-right: 5px; /* 为滚动条预留空间 */
}

.alert-container::-webkit-scrollbar {
    width: 6px;
}

.alert-container::-webkit-scrollbar-thumb {
    background-color: #dcdfe6;
    border-radius: 3px;
}

.alert-container::-webkit-scrollbar-track {
    background-color: #f5f7fa;
}

.list-enter-active,
.list-leave-active {
    transition: all 0.5s ease;
}

.list-enter,
.list-leave-to {
    opacity: 0;
    transform: translateX(30px);
}

.alert-item {
    margin-bottom: 10px;
    border-radius: 8px;
    transition: all 0.3s ease;
}

.alert-item:hover {
    transform: translateX(5px);
}

.no-results {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    width: 100%;
}

.el-select {
    margin: 0 10px;
}
</style>